const Path = require('path');
const { File } = require('ada-util');
const ChildProcess = require('child_process');
const Hooker = require("./../hooker");
const defaultHooker = require("./../config/hooker");

class Installer {
    constructor({ path, agent = 'pnpm', hooker, workspace }) {
        this._path = path;
        this._workspace = workspace;
        this._agent = agent;
        this._hooker = hooker;
        if (!this._hooker) {
            this._hooker = new Hooker({});
            defaultHooker(this._hooker);
        }
    }

    get path() {
        return this._path;
    }

    get hooker() {
        return this._hooker;
    }

    getPackageInfo() {
        return require(Path.resolve(this.path, './package.json'));
    }

    installModules(modules = []) {
        let result = [];
        return (modules || []).reduce((a, module) => {
            return a.then(() => {
                return Promise.resolve().then(() => this.installModule(module)).catch(error => {
                    result.push({ name: module.name, error });
                });
            });
        }, Promise.resolve()).then(() => result);
    }

    installModule({ name, version = 'latest', type = '--save' }) {
        return new Promise((resolve, reject) => {
            try {
                this.hooker.emit('start-install', { name }).then(() => {
                    let args = ["install", `${name}@${version}`, type];
                    ChildProcess.exec(`${this._agent} ${args.join(" ")}`, {
                        encoding: "utf-8",
                        cwd: this.path
                    }, (error, stdout, stderr) => {
                        if (error) {
                            this.hooker.emit('install-error', { name }).then(() => {
                                reject(name);
                            });
                        } else {
                            this.hooker.emit('after-install', { name }).then(() => {
                                resolve();
                            });
                        }
                    });
                });
            } catch (e) {
                console.log(e);
            }
        });
    }

    installModules(modules) {
        let result = [];
        return (modules || []).reduce((a, module) => {
            return a.then(() => {
                return Promise.resolve().then(() => this.installModule(module)).catch(e => {
                    result.push({ name: module.name, error: e });
                });
            });
        }, Promise.resolve()).then(() => result);
    }

    install() {
        return new Promise((resolve, reject) => {
            this.hooker.emit('start-install-all', { name: this.path }).then(() => {
                ChildProcess.exec(`${this._agent} recursive install`, {
                    encoding: "utf-8",
                    cwd: this._workspace
                }, (error, stdout, stderr) => {
                    if (error) {
                        this.hooker.emit('install-all-error', { name: this.path, error: error }).then(() => {
                            reject(error);
                        });
                    } else {
                        this.hooker.emit('after-install-all', { name: this.path }).then(() => {
                            resolve();
                        });
                    }
                });
            });
        });
    }

    checkUnInstalledModules(modules = []) {
        return Promise.resolve((modules || []).filter(({ name, version }) => {
            let _package = new File(Path.resolve(this.path, `./node_modules/${name}/package.json`));
            // if (!version) {
            return !_package.exist;
            // } else {
            //     let target = require(package).version;
            //     return version === target;
            // }
        }));
    }

    hasUnInstallModules(modules = []) {
        return Promise.resolve((modules || []).find(({ name, version }) => {
            let _package = new File(Path.resolve(this.path, `./node_modules/${name}/package.json`));
            // if (!version) {
            return !_package.exist;
            // } else {
            //     let target = require(package).version;
            //     return version === target;
            // }
        }) !== undefined);
    }
}

module.exports = Installer;